﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media.Media3D;
using System.Diagnostics;

namespace Eniux.Windows.ThreeDEngine.Geometry
{
    /// <summary>
    /// 描述一个3D线
    /// </summary>
    public class Ray3D
    {
        #region 构造、析构
        /// <summary>
        /// 默认构造函数
        /// </summary>
        public Ray3D()
        {
        }

        /// <summary>
        /// 有参构造函数
        /// </summary>
        /// <param name="o"></param>
        /// <param name="d"></param>
        public Ray3D(Point3D o, Vector3D d) 
        {
            Origin = o;
            Direction = d;
        }
        
        /// <summary>
        /// 有参构造函数
        /// </summary>
        /// <param name="p0">3D点</param>
        /// <param name="p1">3D点</param>
        public Ray3D(Point3D p0, Point3D p1)
        {
            Origin = p0;
            Direction = p1 - p0;
        }

        #endregion

        #region 公共属性

        /// <summary>
        /// 获取或设置方向
        /// </summary>
        public Vector3D Direction { get; set; }

        /// <summary>
        /// 获取或设置起始点
        /// </summary>
        public Point3D Origin { get; set; }

        #endregion

        #region 公共方法

        /// <summary>
        /// 在线上获取距离指定3D点最近的3D点.
        /// </summary>
        /// <param name="p3">参照3D点</param>
        /// <returns>计算出的3D点</returns>
        public Point3D GetNearest(Point3D p3)
        {
            double l = Direction.LengthSquared;
            Debug.Assert(l != 0);
            double u = ((p3.X - Origin.X) * Direction.X + (p3.Y - Origin.Y) * Direction.Y
                        + (p3.Z - Origin.Z) * Direction.Z) / l;
            return Origin + u * Direction;
        }

        /// <summary>
        /// 在一个平面上找到交集
        /// </summary>
        /// <param name="position">3D点</param>
        /// <param name="normal">法线</param>
        /// <returns>相交3D点</returns>
        public Point3D? PlaneIntersection(Point3D position, Vector3D normal)
        {
            // http://paulbourke.net/geometry/planeline/
            double dn = Vector3D.DotProduct(normal, Direction);
            if (dn == 0)
            {
                return null;
            }

            double u = Vector3D.DotProduct(normal, position - Origin) / dn;
            return Origin + u * Direction;
        }

        #endregion
    }
}
